/*
 * MiddlewareCliente.cpp
 *
 *  Created on: Jan 25, 2010
 *      Author: jgrande
 */

#include "MiddlewareCliente.h"
#include "BrokerCliente.h"

MiddlewareCliente::MiddlewareCliente():
	proxSecuencia(SECUENCIA_INICIAL),
	proxSecuenciaInicializado(true) {

}

MiddlewareCliente::~MiddlewareCliente() {

}

bool MiddlewareCliente::start() {
	struct sigaction oldact;
	struct sigaction newact;

	if(!lineasTCP || !listaReceptores) {
		return false;
	}

	if(setpgrp()==-1) {
		logger << LogType::LOG_ERROR << "error en setpgrp" << logger.err << logger.endl;
		return false;
	}

	sem.init(0);
	if(!sem) {
		logger << LogType::LOG_ERROR << "error inicializando semaforo" << logger.err << logger.endl;
		return false;
	}

	qrcv = MessageQueue(ftok(CLIENTE_FTOK_FILENAME, CLIENTE_QRCV_PROJID), true);
	qsnd = MessageQueue(ftok(CLIENTE_FTOK_FILENAME, CLIENTE_QSND_PROJID), true);
	qalc = MessageQueue(ftok(CLIENTE_FTOK_FILENAME, CLIENTE_QALC_PROJID), true);
	qrng = MessageQueue(ftok(CLIENTE_FTOK_FILENAME, CLIENTE_QRNG_PROJID), true);

	if(!qrcv || !qsnd || !qalc || !qrng) {
		logger << LogType::LOG_ERROR << "error creando colas" << logger.endl;
		if(qrcv) qrcv.remove();
		if(qsnd) qrcv.remove();
		if(qalc) qalc.remove();
		if(qrng) qrng.remove();
		return false;
	}

	memset(&newact, 0, sizeof(newact));
	newact.sa_handler = SIG_DFL;
	sigaction(SIGINT, &newact, &oldact);

	pidALC = brokerClienteALC();
	pidAceptor = brokerClienteAceptor();

	sigaction(SIGINT, &oldact, NULL);

	if(pidALC==-1 || pidAceptor==-1) {
		logger << LogType::LOG_ERROR << "error iniciando procesos" << logger.endl;
		kill(-getpid(), SIGINT);
		qrcv.remove();
		qsnd.remove();
		qalc.remove();
		qrng.remove();
		return false;
	}

	dirLocal = ConfigSingleton::instance().getDirLocal();

	return true;

}

void MiddlewareCliente::finish() {
//	struct sigaction oldact;
//	struct sigaction newact;

	logger << LogType::LOG_DEBUG_0 << "interrumpiendo ALC..." << logger.endl;
	if(kill(pidALC, SIGINT)==-1) {
		logger << LogType::LOG_ERROR << "Error interrumpiendo ALC "
				<< logger.err << logger.endl;
	} else {
		while(waitpid(pidALC, NULL, 0)!=pidALC);
	}

	logger << LogType::LOG_DEBUG_0 << "interrumpiendo aceptor" << logger.endl;
	if(kill(pidAceptor, SIGINT)==-1) {
		logger << LogType::LOG_ERROR << "Error interrumpiendo "
				"aceptor" << logger.err << logger.endl;
	} else {
		while(waitpid(pidAceptor, NULL, 0)!=pidAceptor);
	}

	logger << LogType::LOG_DEBUG_0 << "destruyendo colas" << logger.endl;
	qrcv.remove();
	qsnd.remove();
	qalc.remove();
	qrng.remove();

	logger << LogType::LOG_DEBUG_0 << "destruyendo semaforo" << logger.endl;
	sem.destroy();

	logger << LogType::LOG_INFO << "middleware apagado" << logger.endl;
}

bool MiddlewareCliente::recv(void* datos) {
	PDUPuntoAPunto pdu;

	logger << LogType::LOG_DEBUG_0 << "esperando mensaje unicast..." << logger.endl;
	qrcv.recv(&pdu, SECUENCIA_UNICAST);
	if(!qrcv) {
		logger << LogType::LOG_ERROR <<"error sacando mensaje de cola" << logger.err << logger.endl;
		return false;
	}

	memcpy(datos, pdu.datos, pdu.ldatos);

	return true;
}

bool MiddlewareCliente::recvMulticast(void* datos) {
	PDUPuntoAPunto pdu;

	logger << LogType::LOG_DEBUG_0 << "esperando mensaje multicast..." << logger.endl;

	if(proxSecuenciaInicializado) {
		logger << LogType::LOG_DEBUG_0 << "esperando mensaje multicast número " << proxSecuencia << logger.endl;
		qrcv.recv(&pdu, proxSecuencia++);
	} else {
		proxSecuencia = qrcv.recv(&pdu, SECUENCIA_UNICAST, true) + 1;
		proxSecuenciaInicializado = true;
	}

	if(!qrcv) {
		logger << LogType::LOG_ERROR << "error sacando mensaje de cola" << logger.err << logger.endl;
		return false;
	}

	memcpy(datos, pdu.datos, pdu.ldatos);

	return true;
}

bool MiddlewareCliente::send(const void* datos, Longitud ldatos, Direccion destino,
		Operacion op) {
	if(ldatos>MAX_DATOS) {
		logger << LogType::LOG_ERROR <<
				"error: se intento enviar un mensaje de " << ldatos <<
				" bytes (max: " << MAX_DATOS << " bytes)" << logger.endl;
		return false;
	}

	PDUPuntoAPunto pdu  = {
			{'P', 'P'},						// Protocolo
			1,								// Versión
			destino,						// Destino
			dirLocal,						// Origen
			op,								// Operación
			0,								// número de secuencia
			ldatos,							// Tamaño datos
			{0}
	};

	memcpy(pdu.datos, datos, ldatos);

	logger << LogType::LOG_DEBUG_0 << "enviando mensaje por cola unicast"
			<< logger.endl;

	qalc.send(&pdu, (long)destino);
	if(!qalc) {
		logger << LogType::LOG_ERROR <<
				"error poniendo mensaje en cola" << logger.err << logger.endl;
		return false;
	}

	return true;
}

bool MiddlewareCliente::sendMulticast(const void* datos, Longitud ldatos,
		Direccion destino) {
	if(ldatos>MAX_DATOS) {
		logger << LogType::LOG_ERROR <<
				"error: se intento enviar un mensaje de " << ldatos <<
				" bytes (max: " << MAX_DATOS << " bytes)" << logger.endl;
		return false;
	}

	PDUPuntoAPunto pdu  = {
			{'P', 'P'},						// Protocolo
			1,								// Versión
			destino,						// Destino
			dirLocal,						// Origen
			middleware::common::OP_DATA,	// Operación
			0,								// número de secuencia
			ldatos,							// Tamaño datos
			{0}
	};

	memcpy(pdu.datos, datos, ldatos);

	logger << LogType::LOG_DEBUG_0 << "enviando mensaje por cola multicast"
			<< logger.endl;

	qrng.send(&pdu, (long)DIR_MULTICAST);
	if(!qrng) {
		logger << LogType::LOG_ERROR <<
				"error poniendo mensaje en cola" << logger.err << logger.endl;
		return false;
	}

	return true;
}

bool MiddlewareCliente::send(const Mensaje& msj, Direccion destino, Operacion op){
	if(msj.getTamanio()>MAX_DATOS) {
		logger << LogType::LOG_ERROR <<
				"error: se intento enviar un mensaje de " << msj.getTamanio()
				<< " bytes (max: " << MAX_DATOS << " bytes)" << logger.endl;
		return false;
	}

	uint8_t* buff = new uint8_t[msj.getTamanio()];

	msj.saveToArray(buff);

	this->send(buff, msj.getTamanio(), destino, op);

	delete [] buff;

	return true;
}
